#include <task.h>
#include <desc.h>
#include <share.h>


volatile TASK* current_task = NULL;       // 当前任务指针，永远指向当前任务
volatile U32 current_reg;                   // 当前任务的上下文起始位置
LIST TASK_LIST;                           // 任务链表头


/******************************************************************************
* 函数名称: SwitchTo(TASK* task)
* 功能说明: 切换到任务 task 执行
* 输入参数: TASK*	task	--任务指针
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET SwitchTo(TASK* task)
{
    // 取任务上下文(寄存器)基址
    U32* reg_base = (U32*)(&task->reg);

    // 检查参数合法性
    if(NULL == task || NULL == reg_base)
        return E_ERR;
    
    // 恢复上下文
    asm volatile(
                "movl %0, %%esp\n"      // 先将栈顶指针 esp 指向任务上下文 reg 的起始位置
                "popl %%gs\n"
                "popl %%fs\n"
                "popl %%es\n"
                "popl %%ds\n"
                
                "popal\n"               // popal(gcc) = popad(nasm)
                // "popl %%edi\n"
                // "popl %%esi\n"
                // "popl %%ebp\n"
                // "addl $4, %%esp\n"   // 跳过 esp 寄存器本身的恢复
                // "popl %%ebx\n"
                // "popl %%edx\n"
                // "popl %%ecx\n"
                // "popl %%eax\n"
                
                "iret\n"
                :
                : "r"(reg_base)         // %0 替换成 reg_base
                :
                );
}

/******************************************************************************
* 函数名称: E_RET TaskCreat(TASK* task, TASK_FUNC pFunc, U08 stackAddr, U16 stackSize, U08* name)
* 功能说明: 创建任务
* 输入参数: TASK_FUNC pFunc     --任务函数
    　　　　U08* stackAddr      --任务栈基址
    　　　　U16 stackSize       --任务栈大小
    　　　　U08* name           --任务名称
* 输出参数: TASK*	task        --任务指针
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET TaskCreat(TASK* task, TASK_FUNC pFunc, U08* stackAddr, U16 stackSize, U08* name)
{
    // 检查参数合法性
    if(NULL == task || NULL == pFunc || NULL == stackAddr || 0 == stackSize)
        return E_ERR;

    task->name = name;
    task->stack_addr = stackAddr;
    task->stack_size = stackSize;

    task->reg.cs = LDT_CODE32_SEL;
    task->reg.gs = LDT_VIDEO_SEL;
    task->reg.ds = LDT_DATA32_SEL;
    task->reg.es = LDT_DATA32_SEL;
    task->reg.fs = LDT_DATA32_SEL;
    task->reg.ss = LDT_DATA32_SEL;
    task->reg.esp = (U32)task->stack_addr + task->stack_size; // 栈顶
    task->reg.eip = (U32)pFunc;
    task->reg.eflags = 0x3202;  // IOPL=3: 允许任务（特权级 3）进行 /O 操作; IF=1: 允许外部中断

    return E_OK;
}

/******************************************************************************
* 函数名称: TaskStart(TASK* task)
* 功能说明: 启动第一个任务
* 输入参数: TASK*	task0	--任务指针
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 想要启动所有任务，只要启动第一个任务就可以了，其它任务将由任务调度启动
******************************************************************************/
E_RET TaskStart(TASK* task0)
{
    // 检查参数合法性
    if(NULL == task0)
        return E_ERR;

    current_task = task0;                                       // 当前任务指针指向 task0
    TSS* tss = (TSS*)(*(U32*)TSS_ENTRY_ADDR);               // 找到 TSS
    tss->esp0 = (U32)(&task0->reg) + sizeof(task0->reg);        // TSS.esp0 指向 task0 的上下文数据结构 reg 的末尾   
    current_reg = (U32)&task0->reg;                             // current_reg 指向任务上下文数据结构 reg 的起始位置
    
    asm volatile("sti");                                        // 开中断
    SWITCH_TO(task0);                                           // 切换到 TASK A 执行

    return E_OK;
}